<?php

/**
 * 任务添加
 * 
 *
 * @author chongi
 *
*/

/**
 * 任务属性类
 * @author chongi
 *
 */
class task {
	

	//数据uuid,不需要设置
	public $uuid;
	
	//任务创建时间,需要设置
	public $createTime;
	
	//任务创建人，需要设置
	public $creator;
	
	//任务组，需要设置
	public $taskGroupId;
	
	//任务名称,需要设置
	public $taskName;
	
	//任务文件,需要设置
	public $taskFile;
	
	//任务ID，需要设置
	public $taskId;
	
	//任务脚本名称，不需要设置
	public $taskScriptName;
	
	//任务执行周期类型
	public $taskCycleType=1;
	
	//任务备注，需要设置
	public $remark;
	
	//任务执行类型  不需要设置 1:http 2:command
	public $taskExecType=1;
	
	
	public $taskExecCrontab;
	
	//任务 crontab 不需要设置
	public $taskCrontab;
	
	//任务执行超时时间，可选设置
	public $taskExecTimeout=600;
	
	//任务异常是否通知，可选设置
	public $isNotify=1;
	
	//任务通知次数，可选设置
	public $notifyNum=3;
	
	//任务通知类型，可选设置
	public $notifyType=1;
	
	//任务通知用户，可选设置
	public $notifyUser='';
	
	//任务状态 1:启用 0:禁用 2:运行
	public $useFlg=1;
	
	
	
	const TASK_CYCLE_EVERY_DAY=1;
	
	const TASK_CYCLE_EVERY_WEEK=2;
	
	const TASK_CYCLE_EVERY_MONTH=3;
	
	const TASK_CYCLE_EVERY_YEAR=4;
	
	const TASK_CYCLE_INTERVAL_MIN=5;
	
	const TASK_CYCLE_INTERVAL_HOUR=6;
	
	const TASK_CYCLE_INTERVAL_DAY=7;
	
	
	
	
	/**
	 * 每小时执行一次
	 * @param int $hour
	 * @param int $min
	 */
	public function everyHour($min=0){
		
		$this->taskCrontab=sprintf("%d * * * *",$min);
		
	}
	
	
	/**
	 * 每天执行一次
	 * @param int $hour
	 * @param int $min
	 */
	public function everyDay($hour=0,$min=0){
		
		$this->taskCrontab=sprintf("%d %d * * *",$min,$hour);
		$this->taskCycleType=self::TASK_CYCLE_EVERY_DAY;
	}
	
	/**
	 * 每周执行一次
	 * @param int $hour
	 * @param int $min
	 */
	public function everyWeek($weeks=0,$hour=0,$min=0){
		
		$this->taskCrontab=sprintf("%d %d *  * %d ",$min,$hour,$weeks);
		$this->taskCycleType=self::TASK_CYCLE_EVERY_WEEK;
	}
	
	/**
	 * 每月执行一次
	 * @param int $hour
	 * @param int $min
	 */
	public function everyMonth($days=1,$hour=0,$min=0){
		$this->taskCrontab=sprintf("%d %d %d  * *",$min,$hour,$days);
		$this->taskCycleType=self::TASK_CYCLE_EVERY_MONTH;
	}
	
	
	/**
	 * 每年执行一次
	 * @param int $hour
	 * @param int $min
	 */
	public function everyYear($month=1,$days=1,$hour=0,$min=0){
		$this->taskCrontab=sprintf("%d %d %d  %d * ",$min,$hour,$days,$month);
		$this->taskCycleType=self::TASK_CYCLE_EVERY_YEAR;
		
	}
	
	/**
	 * 间隔多少分钟执行一次
	 * @param int $min
	 */
	public function intervalMin($min=1){
		
		$this->taskCrontab=sprintf("*/%d * * * * ",$min);
		$this->taskCycleType=self::TASK_CYCLE_INTERVAL_MIN;
		
	}
	
	/**
	 * 间隔多少小时执行一次
	 * @param int $min
	 */
	public function intervalHour($min=1){
		
		$this->taskCrontab=sprintf("0 */%d * * *",$min);
		$this->taskCycleType=self::TASK_CYCLE_INTERVAL_HOUR;
		
	}
	
	/**
	 * 间隔多少天执行一次
	 * @param int $min
	 */
	public function intervalDay($day=1){
		$this->taskCrontab=sprintf("0 0 */%d  * * ",$day);
		$this->taskCycleType=self::TASK_CYCLE_INTERVAL_DAY;
		
		
	}
	
	
	public function create(){
		
		
		
			
			$filePos=strpos(basename($this->taskFile),'.php');
			$this->taskScriptName=sprintf('task_%s',substr(basename($this->taskFile),0,$filePos));
			$this->taskCrontab = $this->taskCrontab  .  '	' . $this->taskScriptName;
			$this->taskContent=<<<'EOF'
#!/bin/bash
		
source /etc/profile
source $APP_PATH_BIN/constant.sh
		
EOF;
			$this->taskContent .= sprintf("# 任务名称:%s \n",$this->taskName);
			$this->taskContent .= sprintf("# 任务创建人:%s \n",$this->creator);
			$this->taskContent .= sprintf("# 任务创建时间:%s \n",$this->createTime);
			$this->taskContent .= sprintf("# 任务描述:%s \n",$this->remark);
			$this->taskContent .= sprintf('curl %s/%s?act=%s&taskId=%d&taskCode=%s&taskExecTimeout=%d',constants::get('TASK_SERVER_ADDR'),$this->taskFile,taskHandler::SECRET_KEY,$this->taskId,$this->taskCode,$this->taskExecTimeout);
		
	}
	
	
}


/**
 * 任务cronab类
 * @author chongi
 *
 */
class taskCrontab{
	
	public $taskScriptName='';
	
	public $taskContent;
	
	public $crontab;
	
	const  CRONTAB_USER='wanson';
	
	const  CRONTAB_CONF='/var/spool/cron/wanson';
	
	const  CRONTAB_SPLIT='###';
	
	const  CRONTAB_FILE_PATH='/usr/local/inonesoft/bin/';
	public function save($task){
		$crontabT=array();
		$crontabList=file_get_contents(self::CRONTAB_CONF);
		
		foreach ($crontabList as $crontab){
			list($crontabName,$crontabCode)=explode(self::CRONTAB_SPLIT, $crontab);
			$crontabT[$crontabCode]=$crontab;
		}
		
		if (isset($crontabT[$task['taskCode']])){
			unset($crontabT[$task['taskCode']]);
		}
		$crontabT['taskCode']=sprintf("%s%s%s",$task['taskCrontab'],self::CRONTAB_SPLIT,$task['taskCode']);
		
		if (!file_put_contents(self::CRONTAB_CONF, array_values($crontabT),LOCK_EX)){
			
			return array('code' => 403,'message' => '请稍等，正在添加任务');
		}
		
		if (file_put_contents(sprintf("%s/%s",self::CRONTAB_FILE_PATH,$task['taskScriptName']), $task['taskContent'])){
			return array('code' => 200,'message' => '添加任务成功');
		}
		
	}
	
	
}


/**
 * 任务监控类
 * @author chongi
 *
 */
class taskMonitor{
	
}

/**
 * 任务管理类
 * @author chongi
 *
 */
class taskManager{
	
	private $dbcaObj;
	
	
	
	public function __construct($dbcaObj){
		$this->dbcaObj=$dbcaObj;	
	}
	
	
	
	
	public function save(task $task){
		
		$task->create();
		
		$taskEntityObj=new entity('om_task_attr');
		
		if (isset($task->taskId) && ! empty($task->taskId)){
			$taskEntityObj->taskId=$task->taskId;
			$this->dbcaObj->fetch($taskEntityObj);
		}
		
		$taskAttrList=get_object_vars($task);
		
		foreach ($taskAttrList as $k => $v){
			$taskEntityObj->$k = $v;
		}
		
		$this->dbcaObj->save($taskEntityObj);
		
		
		$ebsigAsyncObj=new ebsigAsync();
		$ebsigAsyncObj->isWait=True;
		$ebsigAsyncObj->callbackUrl=sprintf("%s/api/async/taskCrontab.php",constants::get('TASK_SERVER_ADDR'));
		$taskAttrList['act']=taskHandler::SECRET_KEY;
		$ebsigAsyncObj->push($taskAttrList);
		$result=$ebsigAsyncObj->read();
		
		if (is_array($result) && $result['code'] == 200){
			return True;
		}
		else {
			return False;
		}
		
	}
	
	
	/**
	 * 查询任务
	 */
	public function query($params=NULL){
		
		$taskObjList=array();
		$queryTaskListSql='select * from om_task_attr ';
		
		if (is_array($params)){
			if (isset($params['taskGroupId']) && is_numeric($params['taskGroupId'])){
				$queryTaskListSql  .= sprintf(' where taskGroupId = %d',$params['taskGroupId']); 
			}
		}
		$taskEntityObjList=$this->dbcaObj->stmt_query($queryTaskListSql);
		
		if ( isset($taskEntityObjList)){
			foreach ($taskEntityObjList as $taskEntityObj){
				$taskObjList[]=$taskEntityObj->getFieldsMap();
				
			}
		}
		
		
		return $taskObjList;
		
	}
	

	
	
	
}

/**
 * 任务运行类
 * @author chongi
 *
 */
abstract   class  taskHandler{
	
	//任务执行使用内存，单位为M
	public $memorySize=256;
	
	//任务每次从数据库或文件中读取数据最大行数
	const   MAX_LINE=5000;
	
	const   TASK_STATS_MESSAGE='__task_stats';
	
	const   SECRET_KEY='cTQzMjM0dTIzaHNoZmtzZmp3ZXJuamsK';
	private $_isInit=False;
	private $_isEnd=False;
	
	
	
	//数据库操作句柄
	protected $dbcaObj=NULL;
	
	private $_execTimeout=NULL;
	
	private $_taskCode='';
	
	private $_taskId=NULL;
	
	private $_cacheObj=NULL;
	
	/**
	 * 验证任务调用是否合法
	 * 
	 * @return boolean
	 */
	public  function auth(){
	
		if (! isset($_REQUEST['act']) || $_REQUEST['act'] != self::SECRET_KEY){
			return False;
		}
	
		if (! isset($_REQUEST['taskId']) || empty($_REQUEST['taskId']) ){
			
			return False;
		}
		
		if (! isset($_REQUEST['taskCode']) || empty($_REQUEST['taskCode']) ){
				
			return False;
		}
		
		if (! isset($_REQUEST['taskExecTimeout']) || empty($_REQUEST['taskExecTimeout'])){
			return False;
		}
		
		$this->_execTimeout=$_REQUEST['taskExecTimeout'];
		$this->taskId=$_REQUEST['taskId'];
		$this->_taskCode=$_REQUEST['taskCode'];
		
		return True;
	}
	
	
	/**
	 * 初始化任务方法
	 * @param string $message
	 * @param string $data
	 */
	public function init($message='',$data=NULL){
		
		if ($this->memorySize > 1024){
			throw new Exception('内存申请超过限制');
		}
		
		ini_set('memory_limit',sprintf('%dM',$this->memorySize));
		
		app_log_init(get_class($this),'',$this->dbcaObj);
		app_log_info(sprintf('任务开始执行		%s',$message),$data);

		
		
		$this->_isInit=True;
	}
	
	
	
	/**
	 * 任务启动方法
	 */
	public function start(iDBCA $dbcaObj ){
		
		$this->dbcaObj=$dbcaObj;
		if (! $this->_isInit){
			$this->init();
		}
		try {
			
			$this->run();
		}
		catch(Exception $e){
			
			if (!$this->_isEnd){
				$this->end(sprintf("执行异常,异常信息:%s",$e->getMessage()),$e->getTraceAsString());	
				
				return False;
			}
		}
		
		if (! $this->_isEnd){
			$this->end();
		}
		
		return True;
	}
	
	/**
	 * 任务运行方法
	 */
	public abstract  function run();
	
	/**
	 * 释放数据使用内存
	 * 
	 * @param any $val	任意类型
	 */
	public function clear($val){
		
		if (is_array($val)){
			
			if ($this->isAssoc($val)){
				unset($val);
			}
			else {
				foreach ($val as $v){
					if ($v instanceof  cellBox){
						unset($val);
					}
				}
			}
		}
		
		else {
			unset($val);
		}
		
	}
	
	/**
	 * 任务结束处理方法
	 * @param string $message
	 * @param array $data
	 */
	public function end($message='',$data=NULL){
		app_log_info(sprintf('任务执行结束	%s',$message) ,$data);
		app_log_end();
		
	}
	
	/**
	 * 检查数据是否是关联数组
	 * 
	 * @param array $var
	 * 
	 * @return boolean
	 */
	private function isAssoc(array $var)
	{
		return array_diff_assoc(array_keys($var), range(0, sizeof($var))) ? TRUE : FALSE;
	}
	
	
	
}